/*
 * Copyright © 2011 Nokia Corporation. All rights reserved.
 * Nokia and Nokia Connecting People are registered trademarks of Nokia Corporation. 
 * Oracle and Java are trademarks or registered trademarks of Oracle and/or its
 * affiliates. Other product and company names mentioned herein may be trademarks
 * or trade names of their respective owners. 
 * See LICENSE.TXT for license information.
 */

package com.nokia.example.battletank;

import com.nokia.example.battletank.game.Game;
import com.nokia.example.battletank.game.ProtectedContentException;
import com.nokia.example.battletank.game.audio.AudioManager;
import com.nokia.example.battletank.menu.HelpMenu;
import com.nokia.example.battletank.menu.AboutMenu;
import com.nokia.example.battletank.menu.BattleTankMenu;
import com.nokia.example.battletank.menu.BuyMenu;
import com.nokia.example.battletank.menu.Menu;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.game.GameCanvas;
import javax.microedition.rms.RecordEnumeration;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;

public class BattleTankCanvas extends GameCanvas {
    private static final int MAX_RENDERING_FPS = 20;
    private static final int MAX_LOGIC_FPS = 40;

    private static final int LEFT_SOFTKEY = -6;
    private static final int RIGHT_SOFTKEY = -7;

    private volatile int pointerKeyState = 0;

    private Main main;
    private BattleTankMenu menu;
    private BuyMenu buyMenu;
    private HelpMenu helpMenu;
    private AboutMenu aboutMenu;
    private Game game;
    private PointerEventHandler pointerEventHandler;
    private Timer renderLoop;
    private Timer gameLogicLoop;

    public BattleTankCanvas(Main main) {
        super(false);
        setFullScreenMode(true);
        this.main = main;
    }

    protected void showNotify() {
        if(menu == null) createMenu();        
        if(game == null) createGame();
        if(buyMenu == null) createBuyMenu();
        if(helpMenu == null) createHelpMenu();
        if(aboutMenu == null) createAboutMenu();
        if(!Main.isTrial()) hideBuyOption();
        if(pointerEventHandler == null) createPointerEventHandler();
        startRendering();
        showMenu();
    }

    protected void hideNotify() {
        AudioManager.disableSounds();
        stopRendering();
        stopGameLogic();
    }

    protected void sizeChanged(int w, int h) {
        if(menu != null) menu.setSize(w, h);
        if(buyMenu != null) buyMenu.setSize(w, h);
        if(helpMenu != null) helpMenu.setSize(w, h);
        if(aboutMenu != null) aboutMenu.setSize(w, h);
        if(game != null) game.setViewportSize(w, h);
        if(pointerEventHandler != null) pointerEventHandler.setSize(w, h);
    }

    protected void keyPressed(int key) {
        if(buyMenu.visible) {
            switch(getGameAction(key)) {
                case UP:
                    buyMenu.selectPrev();
                    break;
                case DOWN:
                    buyMenu.selectNext();
                    break;
                case FIRE:
                    buyMenu.clickSelected();
                    break;
            }
        } else if(helpMenu.visible) {
            switch(getGameAction(key)) {
                case UP:
                    helpMenu.selectPrev();
                    break;
                case DOWN:
                    helpMenu.selectNext();
                    break;
                case FIRE:
                    helpMenu.clickSelected();
                    break;
            }
        } else if(aboutMenu.visible) {
            switch(getGameAction(key)) {
                case UP:
                    aboutMenu.selectPrev();
                    break;
                case DOWN:
                    aboutMenu.selectNext();
                    break;
                case FIRE:
                    aboutMenu.clickSelected();
                    break;
            }
        } else if(menu.visible) {
            switch(getGameAction(key)) {
                case UP:
                    menu.selectPrev();
                    break;
                case DOWN:
                    menu.selectNext();
                    break;
                case FIRE:
                    menu.clickSelected();
                    break;
            }
        }
        switch(key) {
            case LEFT_SOFTKEY:
                leftSoftkey();
                break;
            case RIGHT_SOFTKEY:
                rightSoftkey();
                break;
        }
    }

    protected void pointerPressed(int x, int y) {
        if(buyMenu.visible) buyMenu.pointerEvent(Menu.POINTER_PRESSED, x, y);
        else if(helpMenu.visible) helpMenu.pointerEvent(Menu.POINTER_PRESSED, x, y);
        else if(aboutMenu.visible) aboutMenu.pointerEvent(Menu.POINTER_PRESSED, x, y);
        else if(menu.visible) menu.pointerEvent(Menu.POINTER_PRESSED, x, y);
        else pointerEventHandler.pointerPressed(x, y);
    }

    protected void pointerDragged(int x, int y) {
        if(buyMenu.visible) buyMenu.pointerEvent(Menu.POINTER_DRAGGED, x, y);
        else if(helpMenu.visible) helpMenu.pointerEvent(Menu.POINTER_DRAGGED, x, y);
        else if(aboutMenu.visible) aboutMenu.pointerEvent(Menu.POINTER_DRAGGED, x, y);
        else if(menu.visible) menu.pointerEvent(Menu.POINTER_DRAGGED, x, y);
        else pointerEventHandler.pointerDragged(x, y);
    }

    protected void pointerReleased(int x, int y) {
        if(buyMenu.visible) buyMenu.pointerEvent(Menu.POINTER_RELEASED, x, y);
        else if(helpMenu.visible) helpMenu.pointerEvent(Menu.POINTER_RELEASED, x, y);
        else if(aboutMenu.visible) aboutMenu.pointerEvent(Menu.POINTER_RELEASED, x, y);
        else if(menu.visible) menu.pointerEvent(Menu.POINTER_RELEASED, x, y);
        else pointerEventHandler.pointerReleased(x, y);
    }

    private void leftSoftkey() {
        if(!menu.visible && game != null) {
            stopRendering();
            try {
                stopGameLogic();
                game.leftSoftkeyPressed();
                startGameLogic();
            } catch (ProtectedContentException e) {
                newGame();
                showBuyMenu();
            } catch(IOException e) {
                main.showAlertMessage("Level loading failed",
                    Messages.LEVEL_LOAD_ERROR, AlertType.ERROR);
            }
            startRendering();
        }
    }

    private void rightSoftkey() {
        if(buyMenu.visible) {
            hideBuyMenu();
        } else if(helpMenu.visible) {
            hideHelpMenu();
        } else if(aboutMenu.visible) {
            hideAboutMenu();
        } else if(menu.visible) {
            hideMenu();
        } else {
            showMenu();
        }
    }

    private void startRendering() {
        stopRendering();
        final Graphics g = getGraphics();
        renderLoop = new Timer();
        renderLoop.schedule(new TimerTask() {
            public void run() {
                if(buyMenu.visible) buyMenu.render(g);
                else if(helpMenu.visible) helpMenu.render(g);
                else if(aboutMenu.visible) aboutMenu.render(g);
                else if(menu.visible) menu.render(g);
                else game.render(g);
                flushGraphics();
            }
        }, 0, 1000/MAX_RENDERING_FPS);
    }

    private void startGameLogic() {
        stopGameLogic();
        gameLogicLoop = new Timer();
        gameLogicLoop.schedule(new TimerTask() {
            public void run() {
                game.update(getKeyStates());
            }
        }, 0, 1000/MAX_LOGIC_FPS);
    }

    public int getKeyStates() {
        int keyStates = super.getKeyStates();
        if(keyStates != 0) {
            pointerKeyState = 0;
        } else {
            keyStates = pointerKeyState;
            if(pointerKeyState == FIRE_PRESSED) pointerKeyState = 0;
        }
        return keyStates;
    }

    private void stopRendering() {
        if(renderLoop != null) renderLoop.cancel();
    }

    private void stopGameLogic() {
        if(gameLogicLoop != null) gameLogicLoop.cancel();
    }

    private void createMenu() {
        menu = new BattleTankMenu(getWidth(), getHeight(), new Menu.Listener() {
            public void itemClicked(int item) {
                switch(item) {
                    case BattleTankMenu.RESUME:
                        hideMenu();
                        break;
                    case BattleTankMenu.NEWGAME:
                        newGame();
                        hideMenu();
                        break;                    
                    case BattleTankMenu.FULL_VERSION:
                        showBuyMenu();
                        break;
                    case BattleTankMenu.SOUNDS:
                        game.soundsEnabled = menu.toggleSounds();
                        break;
                    case BattleTankMenu.HELP:
                        showHelpMenu();
                        break;
                    case BattleTankMenu.ABOUT:
                        showAboutMenu();
                        break;
                    case BattleTankMenu.EXIT:
                        main.close();
                        break;
                }
            }
        });
        menu.setBuy(Main.isTrial());
    }

    public void showMenu() {
        if(menu.visible) return;
        stopGameLogic();
        menu.setSounds(game.soundsEnabled);
        menu.selectItem(hasPointerEvents() ? -1 : 0);
        menu.visible = true;
    }

    public void hideMenu() {
        hideBuyMenu();
        if(!menu.visible) return;
        menu.visible = false;
        if(AudioManager.areSoundsEnabled() != game.soundsEnabled) {
            AudioManager.setSoundEnabled(game.soundsEnabled);
        }
        startGameLogic();
    }

    private void createBuyMenu() {
        buyMenu = new BuyMenu(getWidth(), getHeight(), new Menu.Listener() {
            public void itemClicked(int item) {
                switch(item) {
                    case BuyMenu.BUY:                        
                        if(Main.purchaseFullVersion()) buyMenu.showWaitIndicator();
                        break;
                    case BuyMenu.RESTORE:                        
                        if(Main.restoreFullVersion()) buyMenu.showWaitIndicator();
                        break;
                    case BuyMenu.BACK:
                        hideBuyMenu();
                        break;
                }
            }
        });
    }

    public void showBuyMenu() {
        showMenu();
        buyMenu.selectItem(hasPointerEvents() ? -1 : 0);
        buyMenu.visible = true;
    }
    
    public void hideBuyMenu() {
        buyMenu.visible = false;
    }

    public void hideBuyMenuWaitIndicator() {
        buyMenu.hideWaitIndicator();
    }

    public void hideBuyOption() {
        menu.setBuy(false);
    }

    private void createHelpMenu() {
        helpMenu = new HelpMenu(getWidth(), getHeight(), hasPointerEvents(), new Menu.Listener() {
            public void itemClicked(int item) {
                switch(item) {
                    case HelpMenu.BACK:
                        hideHelpMenu();
                        break;
                }
            }
        });
    }

    public void showHelpMenu() {
        showMenu();
        helpMenu.selectItem(hasPointerEvents() ? -1 : 0);
        helpMenu.visible = true;
    }

    public void hideHelpMenu() {
        helpMenu.visible = false;
    }
    
    private void createAboutMenu() {
        aboutMenu = new AboutMenu(getWidth(), getHeight(), new Menu.Listener() {
            public void itemClicked(int item) {
                switch(item) {
                    case AboutMenu.BACK:
                        hideAboutMenu();
                        break;
                }
            }
        });
    }

    public void showAboutMenu() {
        showMenu();
        aboutMenu.selectItem(hasPointerEvents() ? -1 : 0);
        aboutMenu.visible = true;
    }

    public void hideAboutMenu() {
        aboutMenu.visible = false;
    }

    private void createGame() {
        game = new Game(getWidth(), getHeight());
        try {
            //RecordStore.deleteRecordStore("GameState"); // Clear state data for testing purposes
            RecordStore gameState = RecordStore.openRecordStore("GameState", true);
            if(gameState.getNumRecords() == 0 || !game.load(gameState.getRecord(getRecordId(gameState)))) {
                newGame();
            }
            gameState.closeRecordStore();
        } catch (RecordStoreException e) {
            newGame();
        }
    }
    
    private void newGame() {
        try {
            game.newGame();
        } catch(ProtectedContentException e) {
            //should not happen as there should be at least one trial level
            throw new RuntimeException("No levels.");
        } catch(IOException e) {
            //should not happen as there should be at least one trial level
            throw new RuntimeException("No levels.");
        }
    }

    public void saveGame() {
        if(game == null) return;
        try {
            RecordStore gameState = RecordStore.openRecordStore("GameState", true);
            if(gameState.getNumRecords() == 0) gameState.addRecord(null, 0, 0);
            byte[] data = game.getState();
            gameState.setRecord(getRecordId(gameState), data, 0, data.length);
            gameState.closeRecordStore();
        } catch (Exception e) {
            try {
                RecordStore.deleteRecordStore("GameState");
            } catch (RecordStoreException rse) {
            }
        }
    }

    private int getRecordId(RecordStore store) throws RecordStoreException {
        RecordEnumeration e = store.enumerateRecords(null, null, false);
        try {
            return e.nextRecordId();
        } finally {
            e.destroy();
        }
    }

    private void createPointerEventHandler() {
        pointerEventHandler = new PointerEventHandler(getWidth(), getHeight(), new PointerEventHandler.Listener() {
            public void onMoveLeft() {
                pointerKeyState = LEFT_PRESSED;
            }

            public void onMoveRight() {
                pointerKeyState = RIGHT_PRESSED;
            }

            public void onMoveUp() {
                pointerKeyState = UP_PRESSED;
            }

            public void onMoveDown() {
                pointerKeyState = DOWN_PRESSED;
            }

            public void onFire() {
                pointerKeyState = FIRE_PRESSED;
            }

            public void onLeftSoftKey() {
                leftSoftkey();
            }

            public void onRightSoftKey() {
                rightSoftkey();
            }
        });
    }
}
